home *** CD-ROM | disk | FTP | other *** search
- /*---------------------------------------------------------------
- XMODEM.C
- By Donald G. Krantz 4/84
- (C) Copyright 1984 Donald G. Krantz
- All Rights Reserved
-
- Christensen Protocol Routines for XMODEM.C
- This file contains a stand-alone Christensen Engine, which
- requires the following interface to another system:
-
- error() is called to terminate a transfer: the parameter is a
- string describing the error condition.
- VOID error( str )
- char *str;
-
- abort() is called at strategic places to determine if the
- local operator wants to quit sending/receiving.
- VOID abort()
-
- rx() picks up a character from the remote.
- char rx()
-
- rxstat() returns TRUE when a character is ready from the
- remote.
- int rxstat()
-
- tx() sends a character to the remote.
- VOID tx( ch )
- char ch;
-
- lcl_str() sends an informational message to the local
- console.
- lcl_str( str )
- char *str;
-
- ---------------------------------------------------------------*/
-
- #include "stdio.h" /* ECO C */
- #include "b:xmodem.h"
-
- /*---------------------------------------------------------------
- txfile() does transmission of a file.
- Calling sequence: "fd" is a file pointer to the open file
- to be transmitted.
-
- References globals: buffer;
- Modifies globals: rec, crc;
- ---------------------------------------------------------------*/
- VOID txfile( fd )
- FILE *fd;
- {
- char ch; /* scratch character */
- char *i; /* buffer pointer (index) */
- int y; /* scratch counter */
-
- rec = 1; /* uses "natural" numbering */
- while( rxstat() )
- rx();
- while( TRUE ) /* handshake w/ receiver */
- {
- if( (ch = wait( 10, TRUE, TRUE )) == NAK )
- {
- crc = FALSE;
- break;
- }
- if( ch == CRC )
- {
- crc = TRUE;
- break;
- }
- abort();
- }
- while( fillbuf( fd, buffer ) ) /* file not empty? */
- {
- txrec( buffer ); /* send record */
- abort();
- }
- tx( EOT );
- while( (ch = wait( 1, TRUE, TRUE )) != ACK )
- {
- tx( EOT ); /* show end */
- }
- fclose( fd ); /* dump file */
- }
-
- /*---------------------------------------------------------------
- txname() transmits a file name from the CP/M fcb format
- parameter 'name'.
-
- References globals: buffer, checksum, crc;
- Modifies globals: crc;
- ---------------------------------------------------------------*/
- VOID txname( name )
- char *name;
- {
- register int i; /* scratch counter */
- char ch; /* scratch char */
- char crcsav; /* holds state of global 'crc' */
-
- crcsav = crc; /* we always use checksum for */
- crc = FALSE; /* name error detection */
- while( TRUE ) /* main loop for retries */
- {
- while( rxstat() )
- rx();
- i = 0; /* retry count */
- while( TRUE )
- {
- if( wait( RETRY, TRUE, TRUE ) == NAK )
- break;
- if( i++ > RETRY )
- error( "Can't send filename" );
- }
- tx( ACK ); /* handshake name request */
- sleep(); /* decent interval */
- clrcrc(); /* clear checksum accumulator */
- for( i = 0 ; i < NAMESIZE ; i++ )/* name loop */
- {
- tx( name[ i ] );
- wait( 10, TRUE, TRUE );/* wait for ACK */
- }
- tx( EoF ); /* name terminated w/EoF*/
- if(wait(10,FALSE,TRUE)==checksum) /* handshake */
- {
- tx( ACK ); /* handshake OK checksum*/
- crc = crcsav; /* replacce 'crc' */
- return; /* normal exit */
- }
- tx( BADNAME ); /* handshake bad chksm */
- }
- }
-
- /*---------------------------------------------------------------
- txrec() transmits a single record, with retransmit on error from
- receiver. Input is a pointer to the I/O buffer.
-
- References globals: rec, crcaccum, checsum, crc;
- Modifies globals: rec;
- ---------------------------------------------------------------*/
- VOID txrec( buf )
- char *buf;
- {
- register int i;
- unsigned cr;
-
- while( TRUE ) /* do it until right */
- {
- sprintf( msg, "\rTransmitting record %d ", rec );
- lcl_str( msg );
- tx( SOH ); /* start of header */
- tx( rec ); /* rec # */
- tx( ~rec ); /* 1's comp */
- clrcrc(); /* clear CRC accum */
- for( i = 0 ; i < RECSIZE ; i++ )
- tx( buf[ i ] ); /* send record */
- updcrc( 0 ); /* finish up CRC */
- updcrc( 0 ); /* again */
- cr = crcaccum; /* save crc lobyte */
- if( crc ) /* send hi byte first */
- {
- tx( crcaccum >> 8 );
- tx( cr );
- }
- else
- tx( checksum );
- if( wait(10, TRUE, TRUE)==ACK)/* quit if correct*/
- break;
- }
- rec++; /* bump record count */
- }
-
- /*---------------------------------------------------------------
- rxname() loads a CP/M style fcb with a filename from remote
- sender. On receipt of an EOT instead of a name character, exits
- ---------------------------------------------------------------*/
- VOID rxname( fcb )
- char *fcb; /* points to CP/M style fcb */
- {
- char *fcbptr; /* index to fcb */
- register int i; /* scratch counter */
- int ch; /* scratch char (must hold ERR) */
- char chksum; /* checksum accumulator */
-
- while( TRUE )
- {
- fcbptr = fcb; /* align index */
- i = RETRY * 5; /* retry for name h.s. */
- while( TRUE ) /* Handshake NAK */
- {
- abort(); /* check operator abort */
- tx( NAK );
- if( wait( 1, FALSE, FALSE) == ACK )
- break;
- if( !( i-- ) )
- error( "Timed out waiting for name" );
- }
- chksum = EoF; /* init checksum */
- for( i = 0 ; i < 34 ; i++ ) /* accept noise */
- {
- if( (ch = wait(1,FALSE,FALSE)) == EOT )
- {
- tx( ACK );
- exit( 0 );
- }
- if( ch == EoF ) /* End of name chars */
- break;
- if( ch != ERROR ) /* not timeout */
- {
- *(fcbptr++) = ch & 0x7F;
- chksum += ch & 0x7F;
- }
- abort(); /* operator abort */
- tx( ACK ); /* handshake name char */
- }
- fcb[ NAMESIZE ] = 0; /* terminate name field */
- do {
- abort(); /* operator abort */
- tx( chksum ); /* handshake checksum */
- } while( (ch = wait(1,FALSE,FALSE)) == ERROR );
- if( ch == ACK ) /* ACK is good name */
- return;
- }
- }
-
- /*---------------------------------------------------------------
- rxfile() receives a file. Input parameter is a file pointer
- to the local file receiving the transmitted file.
-
- References globals: buffer, rec, crcaccum, checksum, crc;
- Modifies globals: buffer, rec;
- ---------------------------------------------------------------*/
- VOID rxfile( fd )
- FILE *fd;
- {
- char ch; /* scratch handshake var */
- char response; /* ACK/NAK/CRC handshake */
- char crcrlo; /* rec'd CRC low byte */
- char crcrhi; /* rec'd CRC hi byte */
- char r1; /* current record number */
- char r2; /* 1's comp record number */
- unsigned int j; /* wait loop timer */
- int i; /* scratch counter */
- register char *bptr; /* buffer index */
-
- while( rxstat() )
- rx();
- rec = 1; /* uses natural numbering */
- if( crc ) /* set initial handshake to */
- response = CRC; /* CRC or checksum */
- else
- response = NAK;
- while( TRUE ) /* record receive loop */
- {
- bptr = buffer; /* align index */
- sprintf( msg, "\rWaiting for record %d ", rec );
- lcl_str( msg );
- for( i=1 ; i <= RETRY * 5 ; i++ )
- {
- abort();
- tx( response ); /* send handshake */
- if( (ch = wait(1,TRUE,TRUE)) == SOH )
- break; /* SOH indicatees rec */
- if( ch == EOT ) /* EOT indicates done */
- {
- fclose( fd );
- tx( ACK ); /* handshake */
- return; /* normal exit */
- }
- if( ch == CAN ) /* Xmit request abort */
- {
- fclose( fd );
- error("\rReceived cancel request");
- }
- if( i == RETRY * 5 ) /* timeout exit */
- error( "Can't sync to sender" );
- }
- r1 = wait(1,FALSE,FALSE); /* record number */
- r2 = wait(1,FALSE,FALSE); /* 1's comp record # */
- while( bptr - buffer < RECSIZE ) /* test count */
- {
- *(bptr++)=wait(1,FALSE,FALSE);/*accept char*/
- }
- if( crc ) /* get hibyte CRC */
- crcrhi = wait(1,FALSE,FALSE);
- crcrlo = wait(1,FALSE,FALSE);/* lobyte CRC or chksm*/
- response = NAK; /* init response */
- if( (~r1 & 0xFF) != (r2 & 0xFF) )
- continue;
- clrcrc(); /* calc checksum/CRC */
- for( j = 0 ; j < RECSIZE ; j++ )
- updcrc( buffer[ j ] );
- updcrc( 0 ); /* required to finish */
- updcrc( 0 ); /* off CRC - why? */
- if( crc ) /* CRC test */
- if( (crcrlo + (crcrhi << 8)) != crcaccum )
- continue;
- if( !crc ) /* checksum test */
- if( crcrlo != checksum )
- continue;
- if( (r1 == (rec - 1) & 0xFF ) ) /* duplicate? */
- {
- response = ACK; /* dup is OK - ACK was */
- continue; /* trashed - ignore it */
- }
- if( r1 != (rec & 0xFF) )/* fatal sequence error */
- error( "File record numbering error" );
- rec++; /* bump record count */
- for( j = 0 ; j < RECSIZE ; j++ ) /* write data */
- putc( buffer[ j ], fd );
- response = ACK; /* normal loop end */
- }
- }
-
- /*---------------------------------------------------------------
- parse() expands non - ambiguous filespecs to the
- standard CP/M fcb format, excluding drive byte.
- Inputs are "normal" filespec (inspec), and exanded fcb.
- ---------------------------------------------------------------*/
- char *parse( inspec, fcb )
- char *inspec, *fcb;
- {
- register int i; /* fcb index */
- int inptr; /* input spec index */
-
- for( i = 0 ; i < NAMESIZE ; i++ )/* blank fill name */
- fcb[ i ] = ' ';
- if( inspec[ 1 ] == ':' ) /* check for drivspec */
- inptr = 2; /* point past drivespec */
- else
- inptr = 0; /* index to start */
- i = 0; /* pointer into fcb */
- while( TRUE )
- switch( inspec[ inptr++ ] )
- {
- case '\0': /* end of input spec */
- fcb[ NAMESIZE ] = 0;
- return( fcb );
- case '.': /* extension spec'ed */
- i = NAMESIZE - 3; /* extension */
- break;
- default:
- if( i < NAMESIZE )
- fcb[ i++ ] = toupper(
- inspec[ inptr - 1 ] );
- }
- }
-
- /*---------------------------------------------------------------
- unparse() reassembles a filename from a CP/M fcb into "normal" or
- cmpressed form, so that our C functions can deal with them.
- Inputs are a pointer to a string to receive the name (name), and
- a pointer to a CP/M style fcb entry (buf).
- ---------------------------------------------------------------*/
- char *unparse( name, buf )
- char *name, *buf;
- {
- register int i; /* 'name' index */
- int j; /* 'buf' index */
-
- i = 0; /* 'driveless' name */
- for( j = 0 ; j < NAMESIZE ; j++ )/* transfer chars */
- {
- if( buf[ j ] != ' ' ) /* (skip spaces) */
- name[ i++ ] = buf[ j ] & 0x7F;
- if( j == NAMESIZE-4 ) /* don't forget dot */
- name[ i++ ] = '.';
- }
- name[ i ] = '\0'; /* terminate string */
- /* eat terminal dot */
- if( *(index( name, '.' ) + 1) == '\0' )
- *(index( name, '.' )) = '\0';
- return( name ); /* return pointer */
- }
-
- /*---------------------------------------------------------------
- fillbuf() loads the I/O buffer with a record from the input file.
- Returns TRUE if data was available, FALSE if no data left.
- ---------------------------------------------------------------*/
- int fillbuf( fd, buffer )
- FILE *fd;
- char *buffer;
- {
- register int i; /* scratch counter */
- int errorchk; /* holds EOF in Eco C */
-
- for( i = 0 ; i < RECSIZE ; i++ )
- {
- if( (errorchk = getc( fd)) == EOF )
- break;
- buffer[ i ] = errorchk;
- }
- if( i == 0 ) /* no data read */
- return( FALSE );
- for( ; i < RECSIZE ; i++ )
- buffer[ i ] = 0; /* zero fill at EOF */
- return( TRUE );
- }
-
- /*---------------------------------------------------------------
- clrcrc() clears the crc accumulator. Not much to it, actually.
-
- References globals:
- Modifies globals: crcaccum, checksum;
- ---------------------------------------------------------------*/
- VOID clrcrc()
- {
- crcaccum =
- checksum = 0;
- }
-
- /*---------------------------------------------------------------
- updcrc() updates the crc accumulator, if 'crc' is TRUE, else
- updates the checksum.
- 'x' is the byte to be added to CRC or checksum.
- CCITT polynomial.
-
- References globals: crc;
- Modifies globals: crcaccum, checksum;
- ---------------------------------------------------------------*/
- VOID updcrc( x )
- char x;
- {
- unsigned shifter, i, flag;
-
- if( crc )
- {
- for( shifter = 0x80 ; shifter ; shifter >>= 1 )
- {
- flag = (crcaccum & 0x8000);
- crcaccum <<= 1;
- crcaccum |= ((shifter & x) ? 1 : 0);
- if( flag )
- crcaccum ^= 0x1021;
- }
- }
- else
- checksum += x;
- }
-
- /*---------------------------------------------------------------
- sleep() does a short delay to account for transmission line
- latency, etc.
- ---------------------------------------------------------------*/
- VOID sleep()
- {
- register unsigned int i;
-
- for( i=0 ; i < MAGIC_NUMBER ; i++ )
- ;
- }
-
- /*---------------------------------------------------------------
- wait() waits for a character - timeout built in. Timeout
- condition causes error return to menu.
- 'time' controls duration of wait.
- 'is_cmd' TRUE indicates that CAN recvd will cause program exit
- 'error_out' TRUE indicates timeout causes program exit
- ---------------------------------------------------------------*/
- int wait( time, is_cmd, error_out )
- int time;
- {
- register unsigned int i; /* loop timer */
- int j; /* timeout count */
- int ch;
-
- j = /* timeout count */
- i = 0; /* inner timeout count */
- while( !rxstat() )
- if( i++ > MAGIC_NUMBER )/* about 1.5 secs */
- if( !(time--) ) /* check retry count */
- if( error_out )
- error( "Receiver timed out" );
- else
- return( ERROR );
- else /* more tries available */
- {
- abort(); /* scan for ^X */
- if( !j ) /* \n first timeout */
- {
- sprintf( msg, "\n" );
- lcl_str( msg );
- }
- sprintf( msg, "\rtimeout %d ", ++j );
- lcl_str( msg );
- i = 0;
- }
- if( !is_cmd )
- return( rx() ); /* send back char */
- if( (ch = rx()) == CAN )
- error( "Received cancellation request" );
- return( ch );
- }
-
- n( rx() ); /* send back char */
- if( (ch = rx()) == CAN )
- error